#if ((FEATURE_MODE == 2) || (FEATURE_MODE == 0))
/**
 @file sys_usw_ipuc.c

 @date 2011-11-30

 @version v2.0

 The file contains all ipuc related function
*/

/****************************************************************************
 *
* Header Files
*
****************************************************************************/

#include "ctc_const.h"
#include "ctc_error.h"
#include "ctc_ipuc.h"
#include "ctc_parser.h"
#include "ctc_debug.h"
#include "ctc_linklist.h"
#include "sys_usw_common.h"
#include "sys_usw_register.h"
#include "sys_usw_ofb.h"
#include "sys_usw_ftm.h"
#include "sys_usw_ipuc.h"
#include "usw/include/drv_common.h"
#include "sys_tsingma_ipuc_tcam.h"
#include "sys_tsingma_nalpm.h"
#include "../arctic/sys_nalpm2.h"


/****************************************************************************
 *
* Global and Declaration
*
*****************************************************************************/
extern sys_ipuc_master_t* p_usw_ipuc_master[];

extern uint8
_sys_nalpm_get_snake_route_num(uint8 lchip, uint8 ln, uint8 sram_type);

/****************************************************************************
 *
* Function
*
*****************************************************************************/

int32
_sys_tsingma_nalpm_get_tcam_block_num(uint8 lchip,
                                uint8 ip_ver,
                                sys_ipuc_route_mode_t route_mode,
                                uint16* max_block_num)
{
    if ((route_mode == SYS_PRIVATE_MODE) && CTC_FLAG_ISSET(p_usw_ipuc_master[lchip]->lookup_mode[ip_ver], SYS_ALPM_LOOKUP))
    {
        if (ip_ver == CTC_IP_VER_4)
        {
            if (p_usw_ipuc_master[lchip]->host_lpm_mode == 0)
            {
                *max_block_num = 1 + 1 + 32;
            }
            else if (p_usw_ipuc_master[lchip]->host_lpm_mode == 1)
            {
                *max_block_num = 1 + 32;
            }
            else
            {
                *max_block_num = 32;
            }
        }
        else
        {
            if (0 == p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM])
            {
                *max_block_num = 65;
            }
            else
            {
                if (p_usw_ipuc_master[lchip]->host_lpm_mode == 0)
                {
                    /*resolve hash conflict + /128 host route + 0~127*/
                    *max_block_num = 1 + 1 + 128;
                }
                else if (p_usw_ipuc_master[lchip]->host_lpm_mode == 1)
                {
                    *max_block_num =  (!p_usw_ipuc_master[lchip]->use_snake64?0:1) + 1 + 128;
                }
                else if (p_usw_ipuc_master[lchip]->host_lpm_mode == 2)
                {
                    *max_block_num =  (!p_usw_ipuc_master[lchip]->use_snake64?0:1) + 128;
                }
            }
        }
    }
    else
    {
         if (ip_ver == CTC_IP_VER_4)
         {
             *max_block_num = 1 + 32;
         }
        else
        {
            if (((0 == p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM]) && (route_mode == SYS_PRIVATE_MODE)) ||
                ((0 == p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_PUB]) && (route_mode == SYS_PUBLIC_MODE)))
            {
                *max_block_num = 1 + 64;
            }
            else
            {
                *max_block_num = 1 + 128;
            }
        }
    }

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "max_block_num=[%d]\n",*max_block_num);
    return CTC_E_NONE;
}

int32
_sys_tsingma_ipuc_tcam_init_private(uint8 lchip, void* ofb_cb)
{
    uint8 ofb_type = 0;
    uint16 block_max[MAX_CTC_IP_VER] = {0};
    uint32 table_size = 0;
    uint32 total_size = 0;
    uint32 tmp_block_max = 0;
    uint32 block_id = 0;
    uint16 vrfid_num[MAX_CTC_IP_VER] = {0};
    uint32 mask_len = 0;
    sys_usw_ofb_param_t ofb_param = {0};
    uint32 average_size = 0;
    uint32 remain_size = 0;
    uint8  tmp_v4_multiple = 1;

    ofb_param.ofb_cb = ofb_cb;
    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);

    vrfid_num[CTC_IP_VER_4] = 1;
    vrfid_num[CTC_IP_VER_6] = 1;

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "ipv4_num=[%d],ipv6_single_num=[%d],ipv6_num=[%d] \n",
                    p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_4][SYS_TBL_TCAM],
                    p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_SHORT],
                    p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM]);

    if (2 != p_usw_ipuc_master[lchip]->host_lpm_mode)
    {
        p_usw_ipuc_master[lchip]->private_ext_v4_block = ((0 == p_usw_ipuc_master[lchip]->host_lpm_mode)?1:0) + 1;
        p_usw_ipuc_master[lchip]->private_ext_v6_block = ((p_usw_ipuc_master[lchip]->use_snake64 || (!p_usw_ipuc_master[lchip]->use_snake64 && 0 == p_usw_ipuc_master[lchip]->host_lpm_mode))?1:0) + 1;
    }
    else
    {
        p_usw_ipuc_master[lchip]->private_ext_v4_block = 0;
        p_usw_ipuc_master[lchip]->private_ext_v6_block = p_usw_ipuc_master[lchip]->use_snake64 ? 1 : 0;
    }

    _sys_tsingma_nalpm_get_tcam_block_num(lchip, CTC_IP_VER_4, SYS_PRIVATE_MODE, &(p_usw_ipuc_master[lchip]->private_max_block_num[CTC_IP_VER_4]));
    _sys_tsingma_nalpm_get_tcam_block_num(lchip, CTC_IP_VER_6, SYS_PRIVATE_MODE, &(p_usw_ipuc_master[lchip]->private_max_block_num[CTC_IP_VER_6]));

    if (p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM])
    {/* tcam ipv6 double key*/
        block_max[CTC_IP_VER_6] = p_usw_ipuc_master[lchip]->private_max_block_num[CTC_IP_VER_6];
        table_size += p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM];
        p_usw_ipuc_master[lchip]->tcam_share_mode[SYS_PRIVATE_MODE] = SYS_SHARE_IPV6;
    }

    if (p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_SHORT])
    {/* enable tcam ipv6 short key*/
        if (p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM])
        {
            p_usw_ipuc_master[lchip]->short_key_boundary[SYS_PRIVATE_MODE] = table_size;
        }

        block_max[CTC_IP_VER_6] = p_usw_ipuc_master[lchip]->private_max_block_num[CTC_IP_VER_6];
        table_size += p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_SHORT];
        p_usw_ipuc_master[lchip]->tcam_share_mode[SYS_PRIVATE_MODE] = SYS_SHARE_ALL;
    }

    if (p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_4][SYS_TBL_TCAM])
    {/* tcam ipv4 */
        block_max[CTC_IP_VER_4] = p_usw_ipuc_master[lchip]->private_max_block_num[CTC_IP_VER_4];
        table_size += p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_4][SYS_TBL_TCAM];
    }

    CTC_ERROR_RETURN(sys_usw_ofb_init(lchip, block_max[CTC_IP_VER_4] + block_max[CTC_IP_VER_6], table_size, &ofb_type));
    p_usw_ipuc_master[lchip]->share_ofb_type[SYS_PRIVATE_MODE] = ofb_type;

    if (p_usw_ipuc_master[lchip]->tcam_share_mode[SYS_PRIVATE_MODE] == SYS_SHARE_IPV6)
    {
        uint32 tmp_size = p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM];

        ofb_param.max_limit_offset = 0;
        ofb_param.multiple = 4;

        table_size = p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM];
        average_size = ((table_size)/(block_max[CTC_IP_VER_6]- vrfid_num[CTC_IP_VER_6])/ofb_param.multiple) *ofb_param.multiple;
        if(average_size == 0)
        {
            average_size = ofb_param.multiple;
        }
        for (block_id = 0; block_id < block_max[CTC_IP_VER_6]; block_id++)
        {   /* 1~47:4   0:vrfid_num*4  65~127:4*/
           // mask_len = (p_usw_ipuc_master[lchip]->host_lpm_mode + 1 + 127 - block_id);
            mask_len = block_max[CTC_IP_VER_6] - block_id;
            if(mask_len == 0)
            {
                ofb_param.size = (tmp_size > vrfid_num[CTC_IP_VER_6]*ofb_param.multiple)?vrfid_num[CTC_IP_VER_6]*ofb_param.multiple:tmp_size;
            }
            else
            {
                ofb_param.size = (tmp_size > average_size)?average_size:tmp_size;
            }

            tmp_size -= ofb_param.size;
            CTC_ERROR_RETURN(sys_usw_ofb_init_offset(lchip, ofb_type, block_id, &ofb_param));
            total_size += ofb_param.size;
        }
        remain_size = table_size - total_size;
    }
    else if (p_usw_ipuc_master[lchip]->tcam_share_mode[SYS_PRIVATE_MODE] == SYS_SHARE_ALL)
    {
        block_id = 0;
        table_size = p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM];

        if (table_size)
        {
            ofb_param.multiple = 4;
            tmp_block_max = block_max[CTC_IP_VER_6]- 65;
            ofb_param.max_limit_offset = table_size - 1;
            ofb_param.size = 4;
            average_size = ((table_size)/(tmp_block_max)/ofb_param.multiple) *ofb_param.multiple;
            for (block_id = 0; block_id < tmp_block_max; block_id++)
            {
                if(block_id== (tmp_block_max-1) )
                {
                    ofb_param.size = table_size - average_size *block_id ;
                }
                else
                {
                    ofb_param.size = average_size;
                }
                SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "--2 init block id: %d, size: %d, table size: %d\n", block_id, ofb_param.size, table_size);
                CTC_ERROR_RETURN(sys_usw_ofb_init_offset(lchip, ofb_type, block_id, &ofb_param));
            }
        }

        ofb_param.max_limit_offset = 0;
        ofb_param.multiple = 2;
        table_size = p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_SHORT];
        average_size = ((table_size - vrfid_num[CTC_IP_VER_6]*ofb_param.multiple) /(block_max[CTC_IP_VER_6]-tmp_block_max-1)/ofb_param.multiple) *ofb_param.multiple;
        for (; block_id < block_max[CTC_IP_VER_6]; block_id++)
        {
            if(block_id == tmp_block_max)
            {
                ofb_param.size =  table_size - average_size*(block_max[CTC_IP_VER_6] -2-tmp_block_max) - vrfid_num[CTC_IP_VER_6]*ofb_param.multiple;
            }
            else if(block_id == block_max[CTC_IP_VER_6] - 1)
            {
                ofb_param.size = vrfid_num[CTC_IP_VER_6]*ofb_param.multiple;
            }
            else
            {
              ofb_param.size = average_size;
            }
            CTC_ERROR_RETURN(sys_usw_ofb_init_offset(lchip, ofb_type, block_id, &ofb_param));
        }
    }

    ofb_param.max_limit_offset = 0;
    ofb_param.multiple = 1;

    table_size = p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_4][SYS_TBL_TCAM] + remain_size;

    tmp_v4_multiple = (p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM]) ? 4:(p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_SHORT]?2:1);
    vrfid_num[CTC_IP_VER_4] = (vrfid_num[CTC_IP_VER_4]+tmp_v4_multiple)/tmp_v4_multiple*tmp_v4_multiple;
    average_size = (table_size - vrfid_num[CTC_IP_VER_4]) /(block_max[CTC_IP_VER_4]-1);
    average_size = (average_size)/tmp_v4_multiple*tmp_v4_multiple;
    for (; block_id < (block_max[CTC_IP_VER_4] + block_max[CTC_IP_VER_6]); block_id++)
    {
        if(block_id == block_max[CTC_IP_VER_6])
        {
            ofb_param.size = table_size - vrfid_num[CTC_IP_VER_4] - average_size*(block_max[CTC_IP_VER_4] - 2);
        }
        else if(block_id == (block_max[CTC_IP_VER_4] + block_max[CTC_IP_VER_6] - 1))
        {
            ofb_param.size = vrfid_num[CTC_IP_VER_4];
        }
        else
        {
            ofb_param.size = average_size;
        }
        CTC_ERROR_RETURN(sys_usw_ofb_init_offset(lchip, ofb_type, block_id, &ofb_param));
    }

    return CTC_E_NONE;
}

int32
_sys_tsingma_ipuc_v4_tcam_get_blockid(uint8 lchip,
                                uint8 masklen,
                                sys_ipuc_route_mode_t route_mode,
                                uint8 hash_conflict,
                                uint8* block_id)
{
    uint16 ipv6_max_block = 0;
    if ((route_mode == SYS_PRIVATE_MODE) && CTC_FLAG_ISSET(p_usw_ipuc_master[lchip]->lookup_mode[CTC_IP_VER_4], SYS_ALPM_LOOKUP))
    {
        if (p_usw_ipuc_master[lchip]->tcam_share_mode[SYS_PRIVATE_MODE] != SYS_SHARE_NONE)
        {
            ipv6_max_block = p_usw_ipuc_master[lchip]->private_max_block_num[CTC_IP_VER_6];
        }

        if (masklen < 32)
        {/*nalpm block*/
            *block_id = p_usw_ipuc_master[lchip]->private_ext_v4_block + (31 - masklen);
        }
        else if(masklen == 32 && hash_conflict)
        {
            /* there will not be hash conflict in host_lpm_mode 2*/
            *block_id = (p_usw_ipuc_master[lchip]->host_lpm_mode?0:1);
        }
        else if(masklen == 32 && !hash_conflict)
        {
            if (2 != p_usw_ipuc_master[lchip]->host_lpm_mode)
            {
                if (0 == p_usw_ipuc_master[lchip]->host_lpm_mode)
                {
                    *block_id = 0;
                }
                else
                {
                    return CTC_E_INVALID_PARAM;
                }
            }
            else
            {
                return CTC_E_INVALID_PARAM;
            }
        }
        else
        {
            return CTC_E_INVALID_PARAM;
        }
    }
    else
    {
        if ((route_mode == SYS_PUBLIC_MODE) && p_usw_ipuc_master[lchip]->tcam_share_mode[SYS_PUBLIC_MODE] != SYS_SHARE_NONE)
        {
            ipv6_max_block = p_usw_ipuc_master[lchip]->public_max_block_num[CTC_IP_VER_6];
        }
        else if (route_mode == SYS_PRIVATE_MODE && p_usw_ipuc_master[lchip]->tcam_share_mode[SYS_PRIVATE_MODE] != SYS_SHARE_NONE)
        {
            ipv6_max_block = p_usw_ipuc_master[lchip]->private_max_block_num[CTC_IP_VER_6];
        }
        *block_id = 32 - masklen;
    }
    *block_id += ipv6_max_block;
    return CTC_E_NONE;
}

int32
_sys_tsingma_ipuc_v6_tcam_get_blockid(uint8 lchip,
                                uint8 masklen,
                                sys_ipuc_route_mode_t route_mode,
                                uint8 hash_conflict,
                                uint8* block_id)
{
    if ((route_mode == SYS_PRIVATE_MODE) && CTC_FLAG_ISSET(p_usw_ipuc_master[lchip]->lookup_mode[CTC_IP_VER_6], SYS_ALPM_LOOKUP))
    {
        if (p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM])
        {
            if (masklen < 128)
            {/*nalpm block*/
                *block_id = p_usw_ipuc_master[lchip]->private_ext_v6_block + (127 - masklen);
            }
            else if(masklen == 128 && hash_conflict)
            {
                /* there will not be hash conflict in host_lpm_mode 2*/
                *block_id = (p_usw_ipuc_master[lchip]->host_lpm_mode?0:1);
            }
            else if(masklen == 128 && !hash_conflict)
            {
                if (2 != p_usw_ipuc_master[lchip]->host_lpm_mode)
                {
                    if (!(!p_usw_ipuc_master[lchip]->use_snake64&& 1 == p_usw_ipuc_master[lchip]->host_lpm_mode))
                    {
                        *block_id = (p_usw_ipuc_master[lchip]->host_lpm_mode?1:0);
                    }
                    else
                    {
                        return CTC_E_INVALID_PARAM;
                    }
                }
                else
                {
                    if (p_usw_ipuc_master[lchip]->use_snake64)
                    {
                        *block_id = 0;
                    }
                    else
                    {
                        return CTC_E_INVALID_PARAM;
                    }
                }
            }
            else
            {
                return CTC_E_INVALID_PARAM;
            }
        }
        else
        {
            if (masklen <= 64)
            {/*nalpm block*/
                *block_id = (64 - masklen);
            }
            else
            {
                return CTC_E_INVALID_PARAM;
            }
        }
    }
    else
    {
        if (masklen > 64 && 
            (((route_mode == SYS_PRIVATE_MODE) && (0 == p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM])) ||
             ((route_mode == SYS_PUBLIC_MODE) && (0 == p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_PUB]))))
        {
            return CTC_E_NO_RESOURCE;
        }
        if (((route_mode == SYS_PRIVATE_MODE) && p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM]) ||
            ((route_mode == SYS_PUBLIC_MODE) && p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_PUB]))
        {
            *block_id = 128 - masklen;
        }
        else
        {
            *block_id = 64 - masklen;
        }
    }
    return CTC_E_NONE;
}

int32
_sys_tsingma_ipuc_tcam_get_blockid(uint8 lchip,
                                uint8 ip_ver,
                                uint8 masklen,
                                sys_ipuc_route_mode_t route_mode,
                                uint8 hash_conflict,
                                uint8* block_id)
{
    CTC_ERROR_RETURN(p_usw_ipuc_master[lchip]->get_tcam_blockid_cb[ip_ver](lchip, masklen, route_mode, hash_conflict, block_id));

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "block_id=[%d]\n",*block_id);

    return CTC_E_NONE;
}

int32
sys_tsingma_ipuc_tcam_get_blockid(uint8 lchip, void *p_data_v, uint8 *p_block_id)
{
    uint8 ip_ver = 0;
    uint8 masklen = 0;
    uint8 hash_conflict = 0;
    sys_ipuc_info_t  *p_ipuc_info    = NULL;
    ctc_ipuc_param_t  *p_ipuc_param    = NULL;
    sys_ipuc_tcam_data_t *p_data = (sys_ipuc_tcam_data_t*)p_data_v;
    sys_ipuc_route_mode_t route_mode = SYS_PRIVATE_MODE;

    if (p_data->key_type == SYS_IPUC_TCAM_FLAG_TCAM)
    {
        p_ipuc_info = (sys_ipuc_info_t*)p_data->info;

        ip_ver = SYS_IPUC_VER(p_ipuc_info);
        masklen = p_ipuc_info->masklen;
        hash_conflict = CTC_FLAG_ISSET(p_ipuc_info->route_flag, SYS_IPUC_FLAG_HASH_CONFLICT);
        route_mode = CTC_FLAG_ISSET(p_ipuc_info->route_flag, SYS_IPUC_FLAG_PUBLIC_ROUTE);
    }
    else if (p_data->key_type == SYS_IPUC_TCAM_FLAG_ALPM)
    {
        p_ipuc_param = p_data->ipuc_param;
        ip_ver = p_ipuc_param->ip_ver;
        masklen = p_data->masklen;
    }

    return _sys_tsingma_ipuc_tcam_get_blockid(lchip, ip_ver, masklen, route_mode, hash_conflict, p_block_id);
}

int32
sys_tsingma_get_tcam_tbl_id(uint8 lchip, uint8 ip_ver, uint8 is_da, sys_ipuc_tbl_type_t tbl_type, uint32* key_id, uint32* ad_id)
{
    uint32 key_tbl_id = 0;
    uint32 ad_tbl_id = 0;

    switch(tbl_type)
    {
        case SYS_TBL_TCAM:
            if (is_da)
            {
                key_tbl_id = (ip_ver == CTC_IP_VER_4) ? DsLpmTcamIpv4HalfKey_t : DsLpmTcamIpv6DoubleKey0_t;
                ad_tbl_id = (ip_ver == CTC_IP_VER_4) ? DsLpmTcamIpv4HalfKeyAd_t : DsLpmTcamIpv6DoubleKey0Ad_t;
            }
            else
            {
                key_tbl_id = (ip_ver == CTC_IP_VER_4) ? DsLpmTcamIpv4SaHalfKey_t : DsLpmTcamIpv6SaDoubleKey0_t;
                ad_tbl_id = (ip_ver == CTC_IP_VER_4) ? DsLpmTcamIpv4SaHalfKeyAd_t : DsLpmTcamIpv6SaDoubleKey0Ad_t;
            }
            break;
        case SYS_TBL_TCAM_PUB:
            if (is_da)
            {
                key_tbl_id = (ip_ver == CTC_IP_VER_4) ? DsLpmTcamIpv4DaPubHalfKey_t : DsLpmTcamIpv6DaPubDoubleKey0_t;
                ad_tbl_id = (ip_ver == CTC_IP_VER_4) ? DsLpmTcamIpv4DaPubHalfKeyAd_t : DsLpmTcamIpv6DaPubDoubleKey0Ad_t;
            }
            else
            {
                key_tbl_id = (ip_ver == CTC_IP_VER_4) ? DsLpmTcamIpv4SaPubHalfKey_t : DsLpmTcamIpv6SaPubDoubleKey0_t;
                ad_tbl_id = (ip_ver == CTC_IP_VER_4) ? DsLpmTcamIpv4SaPubHalfKeyAd_t : DsLpmTcamIpv6SaPubDoubleKey0Ad_t;
            }
            break;
        case SYS_TBL_TCAM_SHORT:
            if (is_da)
            {
                key_tbl_id = DsLpmTcamIpv6SingleKey_t;
                ad_tbl_id = DsLpmTcamIpv6SingleKeyAd_t;
            }
            else
            {
                key_tbl_id = DsLpmTcamIpv6SaSingleKey_t;
                ad_tbl_id = DsLpmTcamIpv6SaSingleKeyAd_t;
            }
            break;
        case SYS_TBL_TCAM_PUB_SHORT:
            if (is_da)
            {
                key_tbl_id = DsLpmTcamIpv6DaPubSingleKey_t;
                ad_tbl_id = DsLpmTcamIpv6DaPubSingleKeyAd_t;
            }
            else
            {
                key_tbl_id = DsLpmTcamIpv6SaPubSingleKey_t;
                ad_tbl_id = DsLpmTcamIpv6SaPubSingleKeyAd_t;
            }
            break;
        default:
            key_tbl_id = 0;
            ad_tbl_id = 0;
            break;
    }

    if (key_id)
    {
        *key_id = key_tbl_id;
    }

    if (ad_id)
    {
        *ad_id = ad_tbl_id;
    }

    return CTC_E_NONE;
}


int32
_sys_tsingma_ipuc_tcam_move(uint8 lchip, uint8 ip_ver, sys_ipuc_route_mode_t route_mode, uint32 new_index, uint32 old_index, uint32 type)
{
    uint32 cmdr, cmdw;
    ds_t lpmtcam_ad;
    ds_t tcamkey, tcammask;
    tbl_entry_t tbl_ipkey;
    sys_ipuc_tbl_type_t tbl_type[2]; /* index 0 is old, index 1 is new */
    uint32 key_id[2], sa_key_id[2];
    uint32 ad_id[2], sa_ad_id[2];
    uint16 key_index[2];
    uint8 use_short_key[2] = {0};
    sys_ipuc_tcam_key_t key;
    uint8 loop_lchip = 0;

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);

    if (new_index == old_index)
    {
        return CTC_E_NONE;
    }

    if (ip_ver == CTC_IP_VER_4)
    {
        key_index[0] = old_index;
        key_index[1] = new_index;
    }
    else
    {
        use_short_key[0] = IS_SHORT_KEY(route_mode, old_index);
        use_short_key[1] = IS_SHORT_KEY(route_mode, new_index);
        key_index[0] = use_short_key[0] ? (old_index/2) : (old_index/4);
        key_index[1] = use_short_key[1] ? (new_index/2) : (new_index/4);
    }

    if (route_mode == SYS_PRIVATE_MODE)
    {
        tbl_type[0] = use_short_key[0] ? SYS_TBL_TCAM_SHORT : SYS_TBL_TCAM;
        tbl_type[1] = use_short_key[1] ? SYS_TBL_TCAM_SHORT : SYS_TBL_TCAM;
    }
    else
    {
        tbl_type[0] = use_short_key[0] ? SYS_TBL_TCAM_PUB_SHORT : SYS_TBL_TCAM_PUB;
        tbl_type[1] = use_short_key[1] ? SYS_TBL_TCAM_PUB_SHORT : SYS_TBL_TCAM_PUB;
    }
    sys_tsingma_get_tcam_tbl_id(lchip, ip_ver, 1, tbl_type[0], &key_id[0], &ad_id[0]);
    sys_tsingma_get_tcam_tbl_id(lchip, ip_ver, 1, tbl_type[1], &key_id[1], &ad_id[1]);

    cmdr = DRV_IOR(ad_id[0], DRV_ENTRY_FLAG);
    cmdw = DRV_IOW(ad_id[1], DRV_ENTRY_FLAG);
    DRV_IOCTL(lchip, old_index, DRV_CMD_BMP_EN(cmdr, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &lpmtcam_ad);
    DO_SLAVE_LOOP_START
    DRV_IOCTL(lchip + loop_lchip, new_index, DRV_CMD_BMP_EN(cmdw, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &lpmtcam_ad);
    DO_SLAVE_LOOP_END

    cmdr = DRV_IOR(key_id[0], DRV_ENTRY_FLAG);
    cmdw = DRV_IOW(key_id[1], DRV_ENTRY_FLAG);
    tbl_ipkey.data_entry = (uint32*)&tcamkey;
    tbl_ipkey.mask_entry = (uint32*)&tcammask;
    DRV_IOCTL(lchip, key_index[0], DRV_CMD_BMP_EN(cmdr, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &tbl_ipkey);

    if (use_short_key[0] != use_short_key[1])
    {
        uint16 vrfid, vrfid_mask;
        ipv6_addr_t ipv6_data, ipv6_mask;
        if (use_short_key[0])
        {
            GetDsLpmTcamIpv6SingleKey(A, ipAddr_f, &tcamkey, ipv6_data);
            GetDsLpmTcamIpv6SingleKey(A, ipAddr_f, &tcammask, ipv6_mask);
            sal_memcpy(&ipv6_data[2], &ipv6_data[0], sizeof(uint32) * 2);
            sal_memset(&ipv6_data[0], 0, sizeof(uint32) * 2);
            sal_memcpy(&ipv6_mask[2], &ipv6_mask[0], sizeof(uint32) * 2);
            sal_memset(&ipv6_mask[0], 0, sizeof(uint32) * 2);

            vrfid = GetDsLpmTcamIpv6SingleKey(V, vrfId_f, &tcamkey);
            vrfid_mask = GetDsLpmTcamIpv6SingleKey(V, vrfId_f, &tcammask);
            sal_memset(&tcamkey, 0, sizeof(tcamkey));
            sal_memset(&tcammask, 0, sizeof(tcammask));
            SetDsLpmTcamIpv6DoubleKey0(A, ipAddr_f, tcamkey, ipv6_data);
            SetDsLpmTcamIpv6DoubleKey0(V, vrfId_f, tcamkey, vrfid);
            SetDsLpmTcamIpv6DoubleKey0(A, ipAddr_f, tcammask, ipv6_mask);
            SetDsLpmTcamIpv6DoubleKey0(V, vrfId_f, tcammask, vrfid_mask);

            SetDsLpmTcamIpv6DoubleKey0(V, l4DestPort_f, tcamkey, 0);
            SetDsLpmTcamIpv6DoubleKey0(V, l4SourcePort_f, tcamkey, 0);
            SetDsLpmTcamIpv6DoubleKey0(V, l4DestPort_f, tcammask, 0);
            SetDsLpmTcamIpv6DoubleKey0(V, l4SourcePort_f, tcammask, 0);

            SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType0_f, tcamkey, 1);
            SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType1_f, tcamkey, 1);
            SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType2_f, tcamkey, 1);
            SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType3_f, tcamkey, 1);

            SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType0_f, tcammask, 0x1);
            SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType1_f, tcammask, 0x1);
            SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType2_f, tcammask, 0x1);
            SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType3_f, tcammask, 0x1);
        }
        else
        {
            GetDsLpmTcamIpv6DoubleKey0(A, ipAddr_f, &tcamkey, ipv6_data);
            GetDsLpmTcamIpv6DoubleKey0(A, ipAddr_f, &tcammask, ipv6_mask);
            sal_memcpy(&ipv6_data[0], &ipv6_data[2], sizeof(uint32) * 2);
            sal_memcpy(&ipv6_mask[0], &ipv6_mask[2], sizeof(uint32) * 2);

            vrfid = GetDsLpmTcamIpv6DoubleKey0(V, vrfId_f, &tcamkey);
            vrfid_mask = GetDsLpmTcamIpv6DoubleKey0(V, vrfId_f, &tcammask);
            sal_memset(&tcamkey, 0, sizeof(tcamkey));
            sal_memset(&tcammask, 0, sizeof(tcammask));
            SetDsLpmTcamIpv6SingleKey(A, ipAddr_f, tcamkey, ipv6_data);
            SetDsLpmTcamIpv6SingleKey(V, vrfId_f, tcamkey, vrfid);
            SetDsLpmTcamIpv6SingleKey(A, ipAddr_f, tcammask, ipv6_mask);
            SetDsLpmTcamIpv6SingleKey(V, vrfId_f, tcammask, vrfid_mask);

            SetDsLpmTcamIpv6SingleKey(V, lpmTcamKeyType0_f, tcamkey, 1);
            SetDsLpmTcamIpv6SingleKey(V, lpmTcamKeyType1_f, tcamkey, 1);
            SetDsLpmTcamIpv6SingleKey(V, lpmTcamKeyType0_f, tcammask, 0x1);
            SetDsLpmTcamIpv6SingleKey(V, lpmTcamKeyType1_f, tcammask, 0x1);
        }
    }

    DO_SLAVE_LOOP_START
    DRV_IOCTL(lchip + loop_lchip, key_index[1], DRV_CMD_BMP_EN(cmdw, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &tbl_ipkey);
    DO_SLAVE_LOOP_END

    if(CTC_FLAG_ISSET(p_usw_ipuc_master[lchip]->lookup_mode[ip_ver], SYS_TCAM_SA_LOOKUP))
    {
        sys_tsingma_get_tcam_tbl_id(lchip, ip_ver, 0, tbl_type[0], &sa_key_id[0], &sa_ad_id[0]);
        sys_tsingma_get_tcam_tbl_id(lchip, ip_ver, 0, tbl_type[1], &sa_key_id[1], &sa_ad_id[1]);

        cmdr = DRV_IOR(sa_ad_id[0], DRV_ENTRY_FLAG);
        cmdw = DRV_IOW(sa_ad_id[1], DRV_ENTRY_FLAG);
        DRV_IOCTL(lchip, old_index, DRV_CMD_BMP_EN(cmdr, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &lpmtcam_ad);
        DO_SLAVE_LOOP_START
        DRV_IOCTL(lchip + loop_lchip, new_index, DRV_CMD_BMP_EN(cmdw, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &lpmtcam_ad);
        DO_SLAVE_LOOP_END

        cmdw = DRV_IOW(sa_key_id[1], DRV_ENTRY_FLAG);

        DO_SLAVE_LOOP_START
        DRV_IOCTL(lchip + loop_lchip, key_index[1], DRV_CMD_BMP_EN(cmdw, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &tbl_ipkey);
        DO_SLAVE_LOOP_END
    }

    key.key_idx = old_index;
    sys_tsingma_ipuc_tcam_write_key2hw( lchip, ip_ver, route_mode ,&key, DO_DEL);
    if(!route_mode && ip_ver && IS_SHORT_KEY(0, new_index) && !IS_SHORT_KEY(0, old_index) && type == SYS_IPUC_TCAM_FLAG_ALPM)
    {
        p_usw_ipuc_master[lchip]->lpm_tcam_used_cnt[ip_ver] -= 2;
    }
    else if(!route_mode && ip_ver && !IS_SHORT_KEY(0, new_index) && IS_SHORT_KEY(0, old_index) && type == SYS_IPUC_TCAM_FLAG_ALPM)
    {
        p_usw_ipuc_master[lchip]->lpm_tcam_used_cnt[ip_ver] += 2;
    }

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "IPUC_TCAM: move from %s[%d] -> %s[%d] \n", DRV_TABLE_NAME(lchip, key_id[0]), old_index, DRV_TABLE_NAME(lchip, key_id[1]), new_index);

    return CTC_E_NONE;
}

int32
sys_tsingma_ipuc_tcam_move(uint8 lchip, uint32 new_index, uint32 old_index, void *pdata)
{
    sys_ipuc_ofb_cb_t *p_ofb_cb = (sys_ipuc_ofb_cb_t *)pdata;
    uint8 ip_ver = CTC_IP_VER_4;
    sys_ipuc_route_mode_t route_mode = SYS_PRIVATE_MODE;
    sys_ipuc_info_t  *p_ipuc_info    = NULL;

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);

    ip_ver = p_ofb_cb->ip_ver;
    route_mode = p_ofb_cb->route_mode;

        _sys_tsingma_ipuc_tcam_move(lchip, ip_ver, route_mode,  new_index,  old_index, p_ofb_cb->type);


    if (p_ofb_cb->type == SYS_IPUC_TCAM_FLAG_TCAM)
    {
        p_ipuc_info = (sys_ipuc_info_t*)p_ofb_cb->user_data;
        p_ipuc_info->key_index = new_index;
    }

    else
    {
        if (p_usw_ipuc_master[lchip]->multi_level)
        {
            sys_nalpm2_tcam_item_t* p_tcam_item = NULL;
            p_tcam_item = (sys_nalpm2_tcam_item_t*)p_ofb_cb->user_data;
            p_tcam_item->tcam_idx = new_index;
        }
        else
        {
            sys_nalpm_tcam_item_t* p_tcam_item = NULL;
            p_tcam_item = (sys_nalpm_tcam_item_t*)p_ofb_cb->user_data;
            p_tcam_item->tcam_idx = new_index;
        }
    }

    return CTC_E_NONE;

}

int32
sys_tsingma_ipuc_alloc_tcam_key_index(uint8 lchip, void *p_data_v)
{
    uint8 ip_ver = 0;
    uint8 block_id = 0;
    uint32 key_index = 0;
    int32 ret = CTC_E_NONE;
    sys_ipuc_ofb_cb_t *p_ofb_cb = NULL;
    sys_ipuc_route_mode_t route_mode = SYS_PRIVATE_MODE;
    sys_ipuc_tcam_data_t *p_data = (sys_ipuc_tcam_data_t*)p_data_v;

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);


    SYS_USW_REGISTER_WB_SYNC_EN(lchip, CTC_FEATURE_IPUC, SYS_WB_APPID_IPUC_SUBID_OFB, 1);

    ip_ver = p_data->ipuc_param->ip_ver;
    route_mode = CTC_FLAG_ISSET(p_data->ipuc_param->route_flag, CTC_IPUC_FLAG_PUBLIC_ROUTE)? SYS_PUBLIC_MODE: SYS_PRIVATE_MODE;

    CTC_ERROR_RETURN(MCHIP_IPUC(lchip)->tcam_get_blockid(lchip, p_data, &block_id));

    p_ofb_cb = mem_malloc2(MEM_IPUC_MODULE, sizeof(sys_ipuc_ofb_cb_t), sys_ipuc_ofb_cb_t);
    if (NULL == p_ofb_cb)
    {
        SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " No memory \n");
        return CTC_E_NO_MEMORY;
    }
    sal_memset(p_ofb_cb, 0, sizeof(sys_ipuc_ofb_cb_t));

    p_ofb_cb->type = p_data->key_type;
    p_ofb_cb->ip_ver = ip_ver;
    p_ofb_cb->route_mode = route_mode;
    p_ofb_cb->masklen = p_data->ipuc_param->masklen;
    p_ofb_cb->user_data = p_data->info;

    if (CTC_WB_ENABLE(lchip) && CTC_WB_STATUS(lchip) == CTC_WB_STATUS_RELOADING)
    {
        key_index = p_data->key_index;
        ret = sys_usw_ofb_alloc_offset_from_position(lchip, p_usw_ipuc_master[lchip]->share_ofb_type[route_mode], block_id, key_index, (void*)p_ofb_cb);
        if (ret)
        {
            mem_free(p_ofb_cb);
            return ret;
        }
    }
    else
    {
        ret = sys_usw_ofb_alloc_offset(lchip, p_usw_ipuc_master[lchip]->share_ofb_type[route_mode], block_id, &key_index, (void*)p_ofb_cb);
        if (ret)
        {
            mem_free(p_ofb_cb);
            return ret;
        }
        p_data->key_index = key_index;
    }

    if (route_mode == SYS_PRIVATE_MODE)
    {
        if ((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, key_index)))
        {
            p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_SHORT]++;
        }
        else
        {
            p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM]++;
        }
    }
    else
    {
        if ((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, key_index)))
        {
            p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB_SHORT]++;
        }
        else
        {
            p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB]++;
        }
    }
    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "IPUC_TCAM: alloc %s %s %s tcam key index = %d, block_id = %d, ofb_type = %d\n", (ip_ver == CTC_IP_VER_4) ? "IPv4" : "IPv6",
                                    (route_mode == SYS_PRIVATE_MODE) ? "private" : "public", ((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, key_index))) ? "ShortKey" : "FullKey",
                                    key_index, block_id, p_usw_ipuc_master[lchip]->share_ofb_type[route_mode]);

    return CTC_E_NONE;
}

int32
sys_tsingma_ipuc_free_tcam_key_index(uint8 lchip, void *p_data_v)
{
    uint8 ip_ver = 0;
    uint8 block_id = 0;
    uint32 key_index = 0;
    sys_ipuc_route_mode_t route_mode = SYS_PRIVATE_MODE;
    sys_ipuc_tcam_data_t *p_data = (sys_ipuc_tcam_data_t*)p_data_v;

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);

    CTC_PTR_VALID_CHECK(p_data);
    CTC_PTR_VALID_CHECK(p_data->info);
    CTC_PTR_VALID_CHECK(p_data->ipuc_param);
    CTC_PTR_VALID_CHECK(MCHIP_IPUC(lchip)->tcam_get_blockid);

    key_index = p_data->key_index;
    ip_ver = p_data->ipuc_param->ip_ver;
    route_mode = CTC_FLAG_ISSET(p_data->ipuc_param->route_flag, CTC_IPUC_FLAG_PUBLIC_ROUTE);

    SYS_USW_REGISTER_WB_SYNC_EN(lchip, CTC_FEATURE_IPUC, SYS_WB_APPID_IPUC_SUBID_OFB, 1);
    CTC_ERROR_RETURN(MCHIP_IPUC(lchip)->tcam_get_blockid(lchip, p_data, &block_id));

    CTC_ERROR_RETURN(sys_usw_ofb_free_offset(lchip, p_usw_ipuc_master[lchip]->share_ofb_type[route_mode], block_id, key_index));
    /*mem_free(p_ofb_cb) in sys_usw_ofb_free_offset() */

    if(MCHIP_IPUC(lchip)->alpm_set_fragment_status)
    {
        MCHIP_IPUC(lchip)->alpm_set_fragment_status(lchip, ip_ver, MERGE_OK);
    }

    if (route_mode == SYS_PRIVATE_MODE)
    {
        if ((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, key_index)))
        {
            p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_SHORT]--;
        }
        else
        {
            p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM]--;
        }
    }
    else
    {
        if ((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, key_index)))
        {
            p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB_SHORT]--;
        }
        else
        {
            p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB]--;
        }
    }

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "IPUC_TCAM: free %s %s %s tcam key index = %d, block_id = %d, ofb_type = %d\n", (ip_ver == CTC_IP_VER_4) ? "IPv4" : "IPv6",
                                    (route_mode == SYS_PRIVATE_MODE) ? "private" : "public", ((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, key_index))) ? "ShortKey" : "FullKey",
                                    key_index, block_id, p_usw_ipuc_master[lchip]->share_ofb_type[route_mode]);

    return CTC_E_NONE;
}

int32
sys_tsingma_ipuc_tcam_move_cb(
                            uint8 lchip,
                            uint32 new_index,
                            uint32 old_index,
                            void *pdata)
{
    uint8 ip_ver = CTC_IP_VER_4;
    sys_ipuc_ofb_cb_t *p_ofb_cb = NULL;
    sys_ipuc_route_mode_t route_mode = SYS_PRIVATE_MODE;

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);

    if (new_index == old_index)
    {
        return CTC_E_NONE;
    }

    p_ofb_cb = (sys_ipuc_ofb_cb_t*)pdata;
    if (NULL == p_ofb_cb)
    {
        return CTC_E_NONE;
    }
    CTC_PTR_VALID_CHECK(p_ofb_cb->user_data);
    CTC_PTR_VALID_CHECK(MCHIP_IPUC(lchip)->tcam_move);

    CTC_ERROR_RETURN(MCHIP_IPUC(lchip)->tcam_move(lchip, new_index, old_index, p_ofb_cb));

    ip_ver = p_ofb_cb->ip_ver;
    route_mode = p_ofb_cb->route_mode;

    if (ip_ver == CTC_IP_VER_6)
    {
        if (route_mode == SYS_PRIVATE_MODE)
        {
            if (IS_SHORT_KEY(route_mode, old_index) && !IS_SHORT_KEY(route_mode, new_index))
            {
                p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_SHORT]--;
                p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM]++;
            }
            else if (!IS_SHORT_KEY(route_mode, old_index) && IS_SHORT_KEY(route_mode, new_index))
            {
                p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_SHORT]++;
                p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM]--;
            }
        }
        else
        {
            if (IS_SHORT_KEY(route_mode, old_index) && !IS_SHORT_KEY(route_mode, new_index))
            {
                p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB_SHORT]--;
                p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB]++;
            }
            else if (!IS_SHORT_KEY(route_mode, old_index) && IS_SHORT_KEY(route_mode, new_index))
            {
                p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB_SHORT]++;
                p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB]--;
            }
        }
    }

    return CTC_E_NONE;
}



int32
sys_tsingma_ipuc_tcam_write_key(uint8 lchip, void *p_data_v)
{
    sys_ipuc_tcam_key_t key;
    uint8 ip_ver = CTC_IP_VER_4;
    uint8 addr_size = 0;
    sys_ipuc_route_mode_t route_mode = SYS_PRIVATE_MODE;
    sys_ipuc_info_t  *p_ipuc_info    = NULL;
    sys_ipuc_tcam_data_t* p_data = (sys_ipuc_tcam_data_t*)p_data_v;
    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);
    CTC_PTR_VALID_CHECK(p_data);
    CTC_PTR_VALID_CHECK(p_data->info);
    p_ipuc_info = (sys_ipuc_info_t*)p_data->info;
    /* write ipuc tcam key entry */
    ip_ver = SYS_IPUC_VER(p_ipuc_info);
    addr_size = (ip_ver == CTC_IP_VER_4) ? sizeof(ip_addr_t) : sizeof(ipv6_addr_t);
    sal_memset(&key, 0 , sizeof(key));
    key.mask_len = p_ipuc_info->masklen;
    key.key_idx = p_ipuc_info->key_index;
    sal_memcpy(&key.ip,  &p_ipuc_info->ip, addr_size);
    key.vrfId = p_ipuc_info->vrf_id;
    key.vrfId_mask =  0x1FFF;

    if (CTC_FLAG_ISSET(p_ipuc_info->route_flag, SYS_IPUC_FLAG_PUBLIC_ROUTE))
    {
         key.vrfId_mask = 0;
         route_mode = SYS_PUBLIC_MODE;
    }

    CTC_ERROR_RETURN(sys_tsingma_ipuc_tcam_write_key2hw( lchip,  ip_ver,  route_mode, &key,  p_data->opt_type));

    return CTC_E_NONE;
}

int32
sys_tsingma_ipuc_tcam_write_key2hw(uint8 lchip, ctc_ip_ver_t ip_ver, sys_ipuc_route_mode_t route_mode, sys_ipuc_tcam_key_t* p_key, sys_ipuc_opt_type_t opt)
{
    tbl_entry_t tbl_ipkey;
    ds_t tcamkey, tcammask;
    uint32 cmd = 0;
    sys_ipuc_tbl_type_t tbl_type;
    uint32 key_idx;
    uint32 da_table, sa_table;
    uint8 loop_lchip = 0;

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);
    CTC_PTR_VALID_CHECK(p_key);
    if(DO_ADD != opt && DO_DEL != opt)
    {
        return CTC_E_INVALID_PARAM;
    }
    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_PARAM, "ip_ver[%d],route_mode[%d],key_idx[%d],mask_len[%d],vrfId[%d]\n",
                        ip_ver,route_mode,p_key->key_idx,p_key->mask_len,p_key->vrfId);
    key_idx = p_key->key_idx;
    if(SYS_PUBLIC_MODE == route_mode)
    {
        if((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, key_idx)))
        {
            tbl_type = SYS_TBL_TCAM_PUB_SHORT;
        }
        else
        {
            tbl_type = SYS_TBL_TCAM_PUB;
        }
    }
    else
    {
        if((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, key_idx)))
        {
            tbl_type = SYS_TBL_TCAM_SHORT;
        }
        else
        {
            tbl_type = SYS_TBL_TCAM;
        }
    }

    sys_tsingma_get_tcam_tbl_id(lchip, ip_ver, 1, tbl_type, &da_table, NULL);
    if(DO_ADD == opt)
    {
        /* write key */
        sal_memset(&tcamkey, 0, sizeof(ds_t));
        sal_memset(&tcammask, 0, sizeof(ds_t));
        tbl_ipkey.data_entry = (uint32*)&tcamkey;
        tbl_ipkey.mask_entry = (uint32*)&tcammask;

        if(CTC_IP_VER_4 == ip_ver)
        {
            ip_addr_t ipv4_mask;
            /*key fields*/
            SetDsLpmTcamIpv4HalfKey(V, ipAddr_f, tcamkey, p_key->ip.ipv4);
            SetDsLpmTcamIpv4HalfKey(V, vrfId_f, tcamkey, p_key->vrfId);
            SetDsLpmTcamIpv4HalfKey(V, lpmTcamKeyType_f, tcamkey, 0);

            /*key mask*/
            IPV4_LEN_TO_MASK(ipv4_mask, p_key->mask_len);
            SetDsLpmTcamIpv4HalfKey(V, ipAddr_f, tcammask, ipv4_mask);
            SetDsLpmTcamIpv4HalfKey(V, vrfId_f, tcammask, p_key->vrfId_mask);
            SetDsLpmTcamIpv4HalfKey(V, lpmTcamKeyType_f, tcammask, 0x1);
        }
        else
        {
            ipv6_addr_t ipv6_mask;
            ipv6_addr_t ipv6_temp;

            if (IS_SHORT_KEY(route_mode, key_idx))
            {
                sal_memset(&ipv6_temp, 0, sizeof(ipv6_addr_t));

                if (p_usw_ipuc_master[lchip]->multi_level || SYS_PUBLIC_MODE == route_mode)
                {
                    ipv6_temp[0] = p_key->ip.ipv6[2];
                    ipv6_temp[1] = p_key->ip.ipv6[3];
                }
                else
                {
                    ipv6_temp[0] = p_key->ip.ipv6[0];
                    ipv6_temp[1] = p_key->ip.ipv6[1];
                }

                key_idx = key_idx/2;
                /*key fields*/
                SetDsLpmTcamIpv6SingleKey(A, ipAddr_f, tcamkey, ipv6_temp);
                SetDsLpmTcamIpv6SingleKey(V, vrfId_f, tcamkey, p_key->vrfId);
                SetDsLpmTcamIpv6SingleKey(V, lpmTcamKeyType0_f, tcamkey, 1);
                SetDsLpmTcamIpv6SingleKey(V, lpmTcamKeyType1_f, tcamkey, 1);

                 /*key mask*/
                IPV6_LEN_TO_MASK(ipv6_mask, p_key->mask_len);
                sal_memcpy(&ipv6_mask[0], &ipv6_mask[2], sizeof(uint32) * 2);

                SetDsLpmTcamIpv6SingleKey(A, ipAddr_f, tcammask, ipv6_mask);
                SetDsLpmTcamIpv6SingleKey(V, vrfId_f, tcammask, p_key->vrfId_mask);
                SetDsLpmTcamIpv6SingleKey(V, lpmTcamKeyType0_f, tcammask, 0x1);
                SetDsLpmTcamIpv6SingleKey(V, lpmTcamKeyType1_f, tcammask, 0x1);
            }
            else
            {
                key_idx = key_idx/4;

                SetDsLpmTcamIpv6DoubleKey0(A, ipAddr_f, tcamkey, p_key->ip.ipv6);
                SetDsLpmTcamIpv6DoubleKey0(V, vrfId_f, tcamkey, p_key->vrfId);
                SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType0_f, tcamkey, 1);
                SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType1_f, tcamkey, 1);
                SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType2_f, tcamkey, 1);
                SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType3_f, tcamkey, 1);

                IPV6_LEN_TO_MASK(ipv6_mask, p_key->mask_len);
                SetDsLpmTcamIpv6DoubleKey0(A, ipAddr_f, tcammask, ipv6_mask);
                SetDsLpmTcamIpv6DoubleKey0(V, vrfId_f, tcammask, p_key->vrfId_mask);
                SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType0_f, tcammask, 0x1);
                SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType1_f, tcammask, 0x1);
                SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType2_f, tcammask, 0x1);
                SetDsLpmTcamIpv6DoubleKey0(V, lpmTcamKeyType3_f, tcammask, 0x1);
            }
        }

        cmd = DRV_IOW(da_table, DRV_ENTRY_FLAG);
    }
    else
    {
        if(CTC_IP_VER_6 == ip_ver)
        {
            if (IS_SHORT_KEY(route_mode, key_idx))
            {
                key_idx = key_idx/2;
            }
            else
            {
                key_idx = key_idx/4;
            }
        }
        cmd = DRV_IOD(da_table, DRV_ENTRY_FLAG);
    }
    DO_SLAVE_LOOP_START
    DRV_IOCTL(lchip + loop_lchip, key_idx, DRV_CMD_BMP_EN(cmd, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &tbl_ipkey);
    DO_SLAVE_LOOP_END

    if(CTC_FLAG_ISSET(p_usw_ipuc_master[lchip]->lookup_mode[ip_ver], SYS_TCAM_SA_LOOKUP))
    {
        sys_tsingma_get_tcam_tbl_id(lchip, ip_ver, 0, tbl_type, &sa_table, NULL);
        if(DO_ADD == opt)
        {
            cmd = DRV_IOW(sa_table, DRV_ENTRY_FLAG);
        }
        else
        {
            cmd = DRV_IOD(sa_table, DRV_ENTRY_FLAG);
        }
        DO_SLAVE_LOOP_START
        DRV_IOCTL(lchip + loop_lchip, key_idx, DRV_CMD_BMP_EN(cmd, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &tbl_ipkey);
        DO_SLAVE_LOOP_END
    }

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "IPUC_TCAM: write %s[%d]\n", DRV_TABLE_NAME(lchip, da_table), key_idx);

    return CTC_E_NONE;

}

int32
sys_tsingma_ipuc_tcam_read_key(uint8 lchip, ctc_ip_ver_t ip_ver, sys_ipuc_route_mode_t route_mode, sys_ipuc_tcam_key_t* p_key)
{
    tbl_entry_t tbl_ipkey;
    ds_t tcamkey, tcammask;
    uint32 cmd = 0;
    sys_ipuc_tbl_type_t tbl_type;
    uint32 key_idx;
    uint32 da_table;

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);
    CTC_PTR_VALID_CHECK(p_key);

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_PARAM, "ip_ver[%d],route_mode[%d],key_idx[%d],mask_len[%d],vrfId[%d]\n",
                        ip_ver,route_mode,p_key->key_idx,p_key->mask_len,p_key->vrfId);
    key_idx = p_key->key_idx;
    if(ip_ver)
    {
        if (IS_SHORT_KEY(route_mode, key_idx))
        {
            key_idx = key_idx/2;
        }
        else
        {
            key_idx = key_idx / 4;
        }
    }
    if(SYS_PUBLIC_MODE == route_mode)
    {
        if((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, p_key->key_idx)))
        {
            tbl_type = SYS_TBL_TCAM_PUB_SHORT;
        }
        else
        {
            tbl_type = SYS_TBL_TCAM_PUB;
        }
    }
    else
    {
        if((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, p_key->key_idx)))
        {
            tbl_type = SYS_TBL_TCAM_SHORT;
        }
        else
        {
            tbl_type = SYS_TBL_TCAM;
        }
    }

    sys_tsingma_get_tcam_tbl_id(lchip, ip_ver, 1, tbl_type, &da_table, NULL);

    sal_memset(&tcamkey, 0, sizeof(ds_t));
    sal_memset(&tcammask, 0, sizeof(ds_t));
    tbl_ipkey.data_entry = (uint32*)&tcamkey;
    tbl_ipkey.mask_entry = (uint32*)&tcammask;

    cmd = DRV_IOR(da_table, DRV_ENTRY_FLAG);

    DRV_IOCTL(lchip, key_idx, DRV_CMD_BMP_EN(cmd, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &tbl_ipkey);

    if (CTC_IP_VER_4 == ip_ver)
    {
        ip_addr_t ipv4_mask;
        /*key fields*/
        GetDsLpmTcamIpv4HalfKey(A, ipAddr_f, tcamkey, &p_key->ip.ipv4);
        GetDsLpmTcamIpv4HalfKey(A, vrfId_f, tcamkey, &p_key->vrfId);

        /*key mask*/
        GetDsLpmTcamIpv4HalfKey(A, ipAddr_f, tcammask, &ipv4_mask);
        IPV4_MASK_TO_LEN(ipv4_mask, p_key->mask_len);
        GetDsLpmTcamIpv4HalfKey(A, vrfId_f, tcammask, &p_key->vrfId_mask);
    }
    else
    {
        ipv6_addr_t ipv6_mask, ipv6_temp;
        sal_memset(&ipv6_mask, 0, sizeof(ipv6_addr_t));
        sal_memset(&ipv6_temp, 0, sizeof(ipv6_addr_t));

        if (IS_SHORT_KEY(route_mode, p_key->key_idx))
        {
            /*key fields*/
            GetDsLpmTcamIpv6SingleKey(A, ipAddr_f, tcamkey, &ipv6_temp);
            if (p_usw_ipuc_master[lchip]->multi_level)
            {
                p_key->ip.ipv6[2] = ipv6_temp[0];
                p_key->ip.ipv6[3] = ipv6_temp[1];
            }
            else
            {
                p_key->ip.ipv6[0] = ipv6_temp[0];
                p_key->ip.ipv6[1] = ipv6_temp[1];
            }
            GetDsLpmTcamIpv6SingleKey(A, vrfId_f, tcamkey, &p_key->vrfId);

            /*key mask*/
            GetDsLpmTcamIpv6SingleKey(A, ipAddr_f, tcammask, &ipv6_mask);
            IPV6_MASK_TO_LEN(ipv6_mask, p_key->mask_len);
            GetDsLpmTcamIpv6SingleKey(A, vrfId_f, tcammask, &p_key->vrfId_mask);
        }
        else
        {
            GetDsLpmTcamIpv6DoubleKey0(A, ipAddr_f, tcamkey, &p_key->ip.ipv6);
            GetDsLpmTcamIpv6DoubleKey0(A, vrfId_f, tcamkey, &p_key->vrfId);

            GetDsLpmTcamIpv6DoubleKey0(A, ipAddr_f, tcammask, &ipv6_mask);
            IPV6_MASK_TO_LEN(ipv6_mask, p_key->mask_len);
            GetDsLpmTcamIpv6DoubleKey0(A, vrfId_f, tcammask, &p_key->vrfId_mask);
        }
    }

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "IPUC_TCAM: read %s[%d]\n", DRV_TABLE_NAME(lchip, da_table), key_idx);

    return CTC_E_NONE;

}

int32
sys_tsingma_ipuc_tcam_write_ad(uint8 lchip, void *p_data_v)
{
    sys_ipuc_info_t  *p_ipuc_info    = NULL;
    sys_ipuc_tcam_ad_t ad;
    uint8 ip_ver = CTC_IP_VER_4;
    sys_ipuc_route_mode_t route_mode = SYS_PRIVATE_MODE;
    sys_ipuc_tcam_data_t *p_data=(sys_ipuc_tcam_data_t*)p_data_v;
    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);

    CTC_PTR_VALID_CHECK(p_data);
    CTC_PTR_VALID_CHECK(p_data->info);

    p_ipuc_info = (sys_ipuc_info_t*)p_data->info;
    ip_ver = SYS_IPUC_VER(p_ipuc_info);

    sal_memset(&ad, 0 , sizeof(ad));
    ad.tcamAd_key_idx = p_ipuc_info->key_index;
    ad.nexthop = p_ipuc_info->ad_index;
    ad.pointer = 0x7FFFF;

    if (CTC_FLAG_ISSET(p_ipuc_info->route_flag, SYS_IPUC_FLAG_PUBLIC_ROUTE))
    {
         route_mode = SYS_PUBLIC_MODE;
    }

    CTC_ERROR_RETURN(sys_tsingma_ipuc_tcam_write_ad2hw( lchip,  ip_ver,  route_mode, &ad,  p_data->opt_type));

    return CTC_E_NONE;

}

int32
sys_tsingma_ipuc_tcam_read_ad(uint8 lchip, ctc_ip_ver_t ip_ver, sys_ipuc_route_mode_t route_mode, sys_ipuc_tcam_ad_t* p_ad)
{
    ds_t lpmtcam_ad;
    sys_ipuc_tbl_type_t tbl_type;
    uint32 ad_table;
    uint32 cmd = 0;

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);

    if (p_ad->tcamAd_key_idx== INVALID_NEXTHOP_OFFSET)
    {
        SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "tcamAd_key_idx is invalid %d\r\n", p_ad->tcamAd_key_idx);
        return CTC_E_INVALID_PARAM;
    }

    if(SYS_PUBLIC_MODE == route_mode)
    {
        if((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, p_ad->tcamAd_key_idx)))
        {
            tbl_type = SYS_TBL_TCAM_PUB_SHORT;
        }
        else
        {
            tbl_type = SYS_TBL_TCAM_PUB;
        }
    }
    else
    {
        if((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, p_ad->tcamAd_key_idx)))
        {
            tbl_type = SYS_TBL_TCAM_SHORT;
        }
        else
        {
            tbl_type = SYS_TBL_TCAM;
        }
    }

    sal_memset(&lpmtcam_ad, 0, sizeof(lpmtcam_ad));
    sys_tsingma_get_tcam_tbl_id(lchip, ip_ver, 1, tbl_type, NULL, &ad_table);
    cmd = DRV_IOR(ad_table, DRV_ENTRY_FLAG);
    DRV_IOCTL(lchip, p_ad->tcamAd_key_idx, DRV_CMD_BMP_EN(cmd, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &lpmtcam_ad);

    p_ad->nexthop = GetDsLpmTcamAd(V, nexthop_f, &lpmtcam_ad);
    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "IPUC_TCAM: nexthop_f[%x] \n",p_ad->nexthop);
    p_ad->pointer = GetDsLpmTcamAd(V, pointer_f, &lpmtcam_ad);
    p_ad->lpmPipelineValid = GetDsLpmTcamAd(V, lpmPipelineValid_f, &lpmtcam_ad);
    p_ad->indexMask = GetDsLpmTcamAd(V, indexMask_f, &lpmtcam_ad);
    p_ad->lpmStartByte = GetDsLpmTcamAd(V, lpmStartByte_f, &lpmtcam_ad);

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "IPUC_TCAM: read %s[%d] \n", DRV_TABLE_NAME(lchip, ad_table), p_ad->tcamAd_key_idx);

    return CTC_E_NONE;
}

int32
sys_tsingma_ipuc_tcam_write_ad2hw(uint8 lchip, ctc_ip_ver_t ip_ver, sys_ipuc_route_mode_t route_mode, sys_ipuc_tcam_ad_t* p_ad, sys_ipuc_opt_type_t opt)
{

    ds_t lpmtcam_ad;
    sys_ipuc_tbl_type_t tbl_type;
    uint32 ad_table, sa_ad_table;
    uint32 cmd = 0;
    uint8 loop_lchip = 0;

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);

    if (p_ad->tcamAd_key_idx== INVALID_NEXTHOP_OFFSET)
    {
        SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "tcamAd_key_idx is invalid %d\r\n", p_ad->tcamAd_key_idx);
        return CTC_E_INVALID_PARAM;
    }
    if(opt != DO_ADD && opt != DO_UPDATE)
    {
        SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "opt invalid \r\n");
        return CTC_E_NONE;
    }

    if(SYS_PUBLIC_MODE == route_mode)
    {
        if((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, p_ad->tcamAd_key_idx)))
        {
            tbl_type = SYS_TBL_TCAM_PUB_SHORT;
        }
        else
        {
            tbl_type = SYS_TBL_TCAM_PUB;
        }
    }
    else
    {
        if((ip_ver == CTC_IP_VER_6) && (IS_SHORT_KEY(route_mode, p_ad->tcamAd_key_idx)))
        {
            tbl_type = SYS_TBL_TCAM_SHORT;
        }
        else
        {
            tbl_type = SYS_TBL_TCAM;
        }
    }

    sal_memset(&lpmtcam_ad, 0, sizeof(lpmtcam_ad));
    sys_tsingma_get_tcam_tbl_id(lchip, ip_ver, 1, tbl_type, NULL, &ad_table);
    cmd = DRV_IOR(ad_table, DRV_ENTRY_FLAG);
    DRV_IOCTL(lchip, p_ad->tcamAd_key_idx, DRV_CMD_BMP_EN(cmd, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &lpmtcam_ad);
    SetDsLpmTcamAd(V, nexthop_f, &lpmtcam_ad, p_ad->nexthop);
    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "IPUC_TCAM: nexthop_f[%x] \n", p_ad->nexthop);
    if(opt == DO_ADD)
    {
        SetDsLpmTcamAd(V, pointer_f, &lpmtcam_ad, p_ad->pointer);
        SetDsLpmTcamAd(V, lpmPipelineValid_f, &lpmtcam_ad, p_ad->lpmPipelineValid);
        SetDsLpmTcamAd(V, indexMask_f, &lpmtcam_ad, p_ad->indexMask);
        SetDsLpmTcamAd(V, lpmStartByte_f, &lpmtcam_ad, p_ad->lpmStartByte);
    }
    cmd = DRV_IOW(ad_table, DRV_ENTRY_FLAG);
    DO_SLAVE_LOOP_START
    DRV_IOCTL(lchip + loop_lchip, p_ad->tcamAd_key_idx, DRV_CMD_BMP_EN(cmd, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &lpmtcam_ad);
    DO_SLAVE_LOOP_END

    if(CTC_FLAG_ISSET(p_usw_ipuc_master[lchip]->lookup_mode[ip_ver], SYS_TCAM_SA_LOOKUP))
    {
        sys_tsingma_get_tcam_tbl_id(lchip, ip_ver, 0, tbl_type, NULL, &sa_ad_table);

        cmd = DRV_IOW(sa_ad_table, DRV_ENTRY_FLAG);
        DO_SLAVE_LOOP_START
        DRV_IOCTL(lchip + loop_lchip, p_ad->tcamAd_key_idx, DRV_CMD_BMP_EN(cmd, MCHIP_FEATURE_PP_EN(lchip,CTC_FEATURE_IPUC)), &lpmtcam_ad);
        DO_SLAVE_LOOP_END
    }
    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "IPUC_TCAM: update %s[%d] \n", DRV_TABLE_NAME(lchip, ad_table), p_ad->tcamAd_key_idx);

    return CTC_E_NONE;
}


int32
sys_tsingma_ipuc_tcam_get_merge_en(uint8 lchip, uint8 ip_ver, uint8 mask_len)
{
   uint8 merge_en = 0;

    if (CTC_FLAG_ISSET(p_usw_ipuc_master[lchip]->lookup_mode[CTC_IP_VER_6], SYS_TCAM_SHORT_LOOKUP))
    {
        if ((p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM] && mask_len > 64 &&
            (p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM]
            - p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_6][SYS_IPUC_TCAM]*4 <= 8)) ||
            (p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM]
        + p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_SHORT]
        + p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_4][SYS_TBL_TCAM]
        - p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_6][SYS_IPUC_TCAM]*4
        - p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_6][SYS_IPUC_TCAM_SHORT]*2
        - p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_4][SYS_IPUC_TCAM] <= 8))
        {
            merge_en = 1;
        }
    }
    else
    {
        if ((p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM] \
        + p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_4][SYS_TBL_TCAM] \
        - p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_6][SYS_IPUC_TCAM]*4\
        - p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_4][SYS_IPUC_TCAM] <= 8))
        {
            merge_en = 1;
        }
    }

   return merge_en;
}


int32
sys_tsingma_ipuc_tcam_show_key(uint8 lchip, void *p_data_v)
{
    sys_ipuc_route_mode_t route_mode = SYS_PRIVATE_MODE;
    sys_ipuc_tbl_type_t tbl_type = 0;
    uint32 key_id = 0;
    uint32 ad_id = 0;
    uint16 key_index = 0;
    uint16 tcam_ad_index = 0;
    uint8 ip_ver = CTC_IP_VER_4;
    sys_ipuc_info_t *p_ipuc_info = NULL;
    sys_ipuc_tcam_data_t *p_data = (sys_ipuc_tcam_data_t*)p_data_v;
    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);

    CTC_PTR_VALID_CHECK(p_data);
    if (p_data->key_type == SYS_IPUC_TCAM_FLAG_TCAM)
    {
        CTC_PTR_VALID_CHECK(p_data->info);
        p_ipuc_info = (sys_ipuc_info_t*)p_data->info;

        ip_ver = SYS_IPUC_VER(p_ipuc_info);
        key_index = p_ipuc_info->key_index;
        if (CTC_FLAG_ISSET(p_ipuc_info->route_flag, SYS_IPUC_FLAG_PUBLIC_ROUTE))
        {
            route_mode = SYS_PUBLIC_MODE;
        }
    }
    else if (p_data->key_type == SYS_IPUC_TCAM_FLAG_ALPM)
    {
        CTC_PTR_VALID_CHECK(p_data->ipuc_param);
        key_index = p_data->key_index;
        ip_ver = p_data->ipuc_param->ip_ver;
    }
    tcam_ad_index = key_index;


    if (ip_ver == CTC_IP_VER_4)
    {
        tbl_type = (route_mode == SYS_PRIVATE_MODE) ? SYS_TBL_TCAM : SYS_TBL_TCAM_PUB;
    }
    else
    {
        if (route_mode == SYS_PRIVATE_MODE)
        {
            tbl_type = IS_SHORT_KEY(route_mode, key_index) ? SYS_TBL_TCAM_SHORT : SYS_TBL_TCAM;
        }
        else
        {
            tbl_type = IS_SHORT_KEY(route_mode, key_index) ? SYS_TBL_TCAM_PUB_SHORT : SYS_TBL_TCAM_PUB;
        }
        key_index = IS_SHORT_KEY(route_mode, key_index) ? (key_index/2) : (key_index/4);
    }

    sys_tsingma_get_tcam_tbl_id(lchip, ip_ver, 1, tbl_type, &key_id, &ad_id);


    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, " --%-30s:%d\n", DRV_TABLE_NAME(lchip, key_id), key_index);
    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, " --%-30s:%d\n", DRV_TABLE_NAME(lchip, ad_id), tcam_ad_index);
    if (CTC_FLAG_ISSET(p_usw_ipuc_master[lchip]->lookup_mode[ip_ver], SYS_TCAM_SA_LOOKUP))
    {
        sys_tsingma_get_tcam_tbl_id(lchip, ip_ver, 0, tbl_type, &key_id, &ad_id);

        SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, " --%-30s:%d\n", DRV_TABLE_NAME(lchip, key_id), key_index);
        SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, " --%-30s:%d\n", DRV_TABLE_NAME(lchip, ad_id), tcam_ad_index);
    }

    return CTC_E_NONE;
}

int32
sys_tsingma_ipuc_tcam_show_status(uint8 lchip, sal_file_t p_f)
{
    uint8 ip_ver = 0;
    uint16 total_size = 0;
    uint16 total_entry = 0;
    uint16 total_usage = 0;
    sys_ipuc_route_mode_t route_mode;

    SYS_DUMP_DB_LOG(p_f, "%-15s\n","Tcam");
    for (route_mode = SYS_PRIVATE_MODE; route_mode < MAX_ROUTE_MODE; route_mode++)
    {
        if (((route_mode == SYS_PRIVATE_MODE) && !CTC_FLAG_ISSET(p_usw_ipuc_master[lchip]->lookup_mode[CTC_IP_VER_4], SYS_TCAM_LOOKUP)) ||
            ((route_mode == SYS_PUBLIC_MODE) && !CTC_FLAG_ISSET(p_usw_ipuc_master[lchip]->lookup_mode[CTC_IP_VER_4], SYS_TCAM_PUB_LOOKUP)))
        {
            continue;
        }

        total_size = 0;
        total_entry = 0;
        total_usage = 0;
        for (ip_ver = CTC_IP_VER_4; ip_ver < MAX_CTC_IP_VER; ip_ver++)
        {
            if (route_mode == SYS_PRIVATE_MODE)
            {
                if (ip_ver == CTC_IP_VER_4)
                {
                    total_size += p_usw_ipuc_master[lchip]->max_entry_num[ip_ver][SYS_TBL_TCAM];
                    total_entry += p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM];
                    total_usage += p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM];
                }
                else
                {
                    total_size += (p_usw_ipuc_master[lchip]->max_entry_num[ip_ver][SYS_TBL_TCAM_SHORT] +
                                                p_usw_ipuc_master[lchip]->max_entry_num[ip_ver][SYS_TBL_TCAM]);
                    total_entry += (p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_SHORT] +
                                                p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM]);
                    total_usage += (p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_SHORT] * 2 +
                                                p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM] * 4);
                }
            }
            else
            {
                if (ip_ver == CTC_IP_VER_4)
                {
                    total_size += p_usw_ipuc_master[lchip]->max_entry_num[ip_ver][SYS_TBL_TCAM_PUB];
                    total_entry += p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB];
                    total_usage += p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB];
                }
                else
                {
                    total_size += (p_usw_ipuc_master[lchip]->max_entry_num[ip_ver][SYS_TBL_TCAM_PUB_SHORT] +
                                                p_usw_ipuc_master[lchip]->max_entry_num[ip_ver][SYS_TBL_TCAM_PUB]);
                    total_entry += (p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB_SHORT] +
                                                p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB]);
                    total_usage += (p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB_SHORT] * 2 +
                                                p_usw_ipuc_master[lchip]->route_stats[ip_ver][SYS_IPUC_TCAM_PUB] * 4);
                }
            }
        }

        SYS_DUMP_DB_LOG(p_f, "%-15s%-4s", (route_mode == SYS_PRIVATE_MODE) ? "--Private" : "--Public", " ");
        SYS_DUMP_DB_LOG(p_f, "%-15d%-4s",  total_size, " ");
        SYS_DUMP_DB_LOG(p_f, "%-15d%-4s%-15d\n", total_entry, " ", total_usage);

        for (ip_ver = CTC_IP_VER_4; ip_ver < MAX_CTC_IP_VER; ip_ver++)
        {
            if (route_mode == SYS_PRIVATE_MODE)
            {
                if (ip_ver == CTC_IP_VER_4)
                {
                    SYS_DUMP_DB_LOG(p_f, "%-15s%-4s", "  IPv4", " ");
                    SYS_DUMP_DB_LOG(p_f, "%-15s%-4s",  "-", " ");
                    SYS_DUMP_DB_LOG(p_f, "%-15d%-4s%-15d\n", p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_4][SYS_IPUC_TCAM], " ",
                        p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_4][SYS_IPUC_TCAM]);
                }
                else
                {
                    SYS_DUMP_DB_LOG(p_f, "%-15s%-4s", "  IPv6", " ");
                    SYS_DUMP_DB_LOG(p_f, "%-15s%-4s",  "-", " ");
                    SYS_DUMP_DB_LOG(p_f, "%-15d%-4s%-15d\n", p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_6][SYS_IPUC_TCAM], " ",
                        p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_6][SYS_IPUC_TCAM] * 4);

                    if (CTC_FLAG_ISSET(p_usw_ipuc_master[lchip]->lookup_mode[CTC_IP_VER_6], SYS_TCAM_SHORT_LOOKUP))
                    {
                        SYS_DUMP_DB_LOG(p_f, "%-15s%-4s", "  IPv6-Short", " ");
                        SYS_DUMP_DB_LOG(p_f, "%-15s%-4s",  "-", " ");
                        SYS_DUMP_DB_LOG(p_f, "%-15d%-4s%-15d\n", p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_6][SYS_IPUC_TCAM_SHORT], " ",
                            p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_6][SYS_IPUC_TCAM_SHORT] * 2);
                    }
                }
            }
            else
            {
                if (ip_ver == CTC_IP_VER_4)
                {
                    SYS_DUMP_DB_LOG(p_f, "%-15s%-4s", "  IPv4", " ");
                    SYS_DUMP_DB_LOG(p_f, "%-15s%-4s",  "-", " ");
                    SYS_DUMP_DB_LOG(p_f, "%-15d%-4s%-15d\n", p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_4][SYS_IPUC_TCAM_PUB], " ",
                        p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_4][SYS_IPUC_TCAM_PUB]);
                }
                else
                {
                    SYS_DUMP_DB_LOG(p_f, "%-15s%-4s", "  IPv6", " ");
                    SYS_DUMP_DB_LOG(p_f, "%-15s%-4s", "-", " ");
                    SYS_DUMP_DB_LOG(p_f, "%-15d%-4s%-15d\n", p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_6][SYS_IPUC_TCAM_PUB], " ",
                        p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_6][SYS_IPUC_TCAM_PUB] * 4);

                    if (CTC_FLAG_ISSET(p_usw_ipuc_master[lchip]->lookup_mode[CTC_IP_VER_6], SYS_TCAM_SHORT_LOOKUP))
                    {
                        SYS_DUMP_DB_LOG(p_f, "%-15s%-4s", "  IPv6-Short", " ");
                        SYS_DUMP_DB_LOG(p_f, "%-15s%-4s", "-", " ");
                        SYS_DUMP_DB_LOG(p_f, "%-15d%-4s%-15d\n", p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_6][SYS_IPUC_TCAM_PUB_SHORT], " ",
                            p_usw_ipuc_master[lchip]->route_stats[CTC_IP_VER_6][SYS_IPUC_TCAM_PUB_SHORT] * 2);
                    }
                }
            }
        }
   }
    return CTC_E_NONE;
}

int32
_sys_tsingma_ipuc_tcam_init_public(uint8 lchip, void* ofb_cb)
{
    uint8 block_id = 0;
    uint8 ofb_type = 0;
    uint8 tmp_block_max = 0;
    uint16 step = 0;
    uint16 block_max[MAX_CTC_IP_VER] = {0};
    uint32 total_size = 0;
    uint32 table_size = 0;
    sys_ipuc_route_mode_t route_mode = SYS_PUBLIC_MODE;
    sys_usw_ofb_param_t ofb_param = {0};
    ofb_param.ofb_cb = ofb_cb;

    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);

    _sys_tsingma_nalpm_get_tcam_block_num(lchip, CTC_IP_VER_4, SYS_PUBLIC_MODE, &p_usw_ipuc_master[lchip]->public_max_block_num[CTC_IP_VER_4]);
    _sys_tsingma_nalpm_get_tcam_block_num(lchip, CTC_IP_VER_6, SYS_PUBLIC_MODE, &p_usw_ipuc_master[lchip]->public_max_block_num[CTC_IP_VER_6]);

    if (p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_PUB])
    {/* don't enable tcam ipv6 short key*/
        block_max[CTC_IP_VER_6] = p_usw_ipuc_master[lchip]->public_max_block_num[CTC_IP_VER_6];
        table_size += p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_PUB];
        p_usw_ipuc_master[lchip]->tcam_share_mode[route_mode] = SYS_SHARE_IPV6;
    }

    if (p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_PUB_SHORT])
    {/* enable tcam ipv6 short key*/
        if (p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_PUB])
        {
            p_usw_ipuc_master[lchip]->tcam_share_mode[route_mode] = SYS_SHARE_ALL;
            p_usw_ipuc_master[lchip]->short_key_boundary[route_mode] = table_size;
        }
        block_max[CTC_IP_VER_6] = p_usw_ipuc_master[lchip]->public_max_block_num[CTC_IP_VER_6];
        table_size += p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_PUB_SHORT];
        p_usw_ipuc_master[lchip]->tcam_share_mode[route_mode] = SYS_SHARE_ALL;
    }

    if (p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_4][SYS_TBL_TCAM_PUB])
    {/* don't enable tcam ipv6 */
        block_max[CTC_IP_VER_4] = p_usw_ipuc_master[lchip]->public_max_block_num[CTC_IP_VER_4];
        table_size += p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_4][SYS_TBL_TCAM_PUB];
    }

    CTC_ERROR_RETURN(sys_usw_ofb_init(lchip, block_max[CTC_IP_VER_4] + block_max[CTC_IP_VER_6], table_size, &ofb_type));
    p_usw_ipuc_master[lchip]->share_ofb_type[route_mode] = ofb_type;

    if (p_usw_ipuc_master[lchip]->tcam_share_mode[route_mode] == SYS_SHARE_IPV6)
    {
        ofb_param.max_limit_offset = 0;
        ofb_param.multiple = 4;
        table_size = p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_PUB];
        tmp_block_max = block_max[CTC_IP_VER_6];
        step = table_size/tmp_block_max;
        step = step ? (step/ofb_param.multiple) * ofb_param.multiple : ofb_param.multiple;
        ofb_param.size = step;
        for (block_id = 0; block_id < block_max[CTC_IP_VER_6]; block_id++)
        {
            if (block_id == block_max[CTC_IP_VER_6] - 1)
            {
                ofb_param.size = table_size - total_size;
            }

            if ((total_size + ofb_param.size) > table_size)
            {
                ofb_param.size = (total_size < table_size) ? (table_size - total_size) : 0;
            }

            CTC_ERROR_RETURN(sys_usw_ofb_init_offset(lchip, ofb_type, block_id, &ofb_param));
            total_size += ofb_param.size;
        }
    }
    else if (p_usw_ipuc_master[lchip]->tcam_share_mode[route_mode] == SYS_SHARE_ALL)
    {
        block_id = 0;
        table_size = p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_PUB];
        /* masklen 0-64 */
        if (table_size)
        {
            ofb_param.multiple = 4;
            tmp_block_max = block_max[CTC_IP_VER_6]- 65;
            ofb_param.max_limit_offset = table_size - 1;
            ofb_param.size = 4;
            step = (table_size)/(tmp_block_max);
            step = step ? (step/ofb_param.multiple) * ofb_param.multiple : ofb_param.multiple;
            for (block_id = 0; block_id < tmp_block_max; block_id++)
            {
                if(block_id== (tmp_block_max-1) )
                {
                    ofb_param.size = table_size - step *block_id ;
                }
                else
                {
                    ofb_param.size = step;
                }
                CTC_ERROR_RETURN(sys_usw_ofb_init_offset(lchip, ofb_type, block_id, &ofb_param));
            }
        }

        total_size = 0;
        ofb_param.max_limit_offset = 0;
        ofb_param.multiple = 2;
        table_size = p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_6][SYS_TBL_TCAM_PUB_SHORT];
        step = table_size/(65 - 1);
        step = step ? (step/ofb_param.multiple) * ofb_param.multiple : ofb_param.multiple;
        ofb_param.size = step;
        for (; block_id < block_max[CTC_IP_VER_6]; block_id++)
        {
            if (block_id == block_max[CTC_IP_VER_6] -1)
            {
                ofb_param.size = table_size - total_size;
            }

            if ((total_size + ofb_param.size) > table_size)
            {
                ofb_param.size = (total_size < table_size) ? (table_size - total_size) : 0;
            }

            CTC_ERROR_RETURN(sys_usw_ofb_init_offset(lchip, ofb_type, block_id, &ofb_param));
            total_size += ofb_param.size;
        }
    }

    total_size = 0;
    ofb_param.max_limit_offset = 0;
    ofb_param.multiple = 1;
    table_size = p_usw_ipuc_master[lchip]->max_entry_num[CTC_IP_VER_4][SYS_TBL_TCAM_PUB];
    step = table_size/(33 - 1);
    if (p_usw_ipuc_master[lchip]->tcam_share_mode[route_mode] == SYS_SHARE_IPV6)
    {
        step = step ? (step/4) * 4 : 4;
    }
    ofb_param.size = step;

    for (; block_id < (block_max[CTC_IP_VER_4] + block_max[CTC_IP_VER_6]); block_id++)
    {
        if (block_id == block_max[CTC_IP_VER_4] + block_max[CTC_IP_VER_6] - 1)
        {
            ofb_param.size = table_size - total_size;
        }

        if ((total_size + ofb_param.size) > table_size)
        {
            ofb_param.size = (total_size < table_size) ? (table_size - total_size) : 0;
        }

        CTC_ERROR_RETURN(sys_usw_ofb_init_offset(lchip, ofb_type, block_id, &ofb_param));
        total_size += ofb_param.size;
    }

    return CTC_E_NONE;
}

int32
sys_tsingma_ipuc_tcam_init(uint8 lchip, uint8 route_mode, void* ofb_cb_fn)
{
    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);

    p_usw_ipuc_master[lchip]->get_tcam_blockid_cb[CTC_IP_VER_4] = _sys_tsingma_ipuc_v4_tcam_get_blockid;
    p_usw_ipuc_master[lchip]->get_tcam_blockid_cb[CTC_IP_VER_6] = _sys_tsingma_ipuc_v6_tcam_get_blockid;

    if (route_mode == SYS_PRIVATE_MODE)
    {
        return _sys_tsingma_ipuc_tcam_init_private(lchip, ofb_cb_fn);
    }
    else
    {
        return _sys_tsingma_ipuc_tcam_init_public(lchip, ofb_cb_fn);
    }

    return CTC_E_NONE;
}

int32
sys_tsingma_ipuc_tcam_deinit(uint8 lchip, uint8 route_mode)
{
    SYS_IPUC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n",__FUNCTION__);
    if (p_usw_ipuc_master[lchip]->share_ofb_type[route_mode])
    {
        sys_usw_ofb_deinit(lchip, p_usw_ipuc_master[lchip]->share_ofb_type[route_mode],0);
    }

    return CTC_E_NONE;
}
#endif

